﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System.Text;

namespace AdminBase.Common.Attributes
{
    /// <summary>
    /// 幂等过滤器，用于实现幂等性控制
    /// </summary>
    public class IdempotentAttributeFilter : IActionFilter, IResultFilter
    {
        private readonly IDistributedCache _distributedCache;
        private bool _isIdempotencyCache = false;
        const string IdempotencyKeyHeaderName = "Authorization";
        private string _idempotencyKey; // 幂等性键

        /// <summary>
        /// 构造器，初始化分布式缓存实例
        /// </summary>
        /// <param name="distributedCache">分布式缓存的实例</param>
        public IdempotentAttributeFilter(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }

        /// <summary>
        /// 在动作方法执行之前调用
        /// </summary>
        /// <param name="context">动作执行上下文，包含请求、路由、模型状态等信息</param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // 从请求头中获取幂等性键
            Microsoft.Extensions.Primitives.StringValues idempotencyKeys;
            context.HttpContext.Request.Headers.TryGetValue(IdempotencyKeyHeaderName, out idempotencyKeys);
            _idempotencyKey = idempotencyKeys.ToString();

            // 从分布式缓存中获取与幂等性键相关联的缓存数据
            var cacheData = _distributedCache.GetString(GetDistributedCacheKey(context));
            if (cacheData != null)
            {
                context.Result = JsonConvert.DeserializeObject<ObjectResult>(cacheData);
                // 标记为已命中缓存
                _isIdempotencyCache = true;
                return;
            }
        }

        /// <summary>
        /// 在动作结果执行之后调用
        /// </summary>
        /// <param name="context">结果执行上下文，包含执行结果、异常等信息</param>
        public void OnResultExecuted(ResultExecutedContext context)
        {
            // 如果已命中幂等性缓存，则直接返回，不执行缓存操作
            if (_isIdempotencyCache)
            {
                return;
            }

            // 获取当前执行上下文的结果
            var contextResult = context.Result;

            DistributedCacheEntryOptions cacheOptions = new DistributedCacheEntryOptions();
            //相对过期时间
            //cacheOptions.SlidingExpiration = TimeSpan.FromSeconds(10);
            //绝对过期时间
            //cacheOptions.AbsoluteExpirationRelativeToNow = new TimeSpan(0, 1, 0);
            // 根据请求方法设置不同的绝对过期时间
            cacheOptions.AbsoluteExpirationRelativeToNow = context.HttpContext.Request.Method == HttpMethod.Get.Method
                ? TimeSpan.FromMinutes(5)
                : TimeSpan.FromMinutes(1);

            //缓存:
            _distributedCache.SetString(GetDistributedCacheKey(context), JsonConvert.SerializeObject(contextResult), cacheOptions);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public void OnResultExecuting(ResultExecutingContext context) { }

        /// <summary>
        /// 幂等性键
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private string GetDistributedCacheKey(FilterContext context)
        {
            var requestBody = string.Empty;
            if (context.HttpContext.Request.Method == HttpMethod.Post.Method)
            {
                using (var reader = new StreamReader(context.HttpContext.Request.Body))
                {
                    requestBody = reader.ReadToEndAsync().Result;
                }
            }

            return $"Idempotency:{_idempotencyKey}:{context.HttpContext.Request.Method}:{context.HttpContext.Request.Path}:{requestBody}";
        }
    }
}